/**
 * This file is part of Megasquirt Logger for PalmOS.
 * Copyright (C) 2011 Andrew Beard
 *
 * Megasquirt Logger is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Megasquirt Logger is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Megasquirt Logger.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "LogDatabase.h"

#include <StringMgr.h>
#include <MemoryMgr.h>

/**
 * Constructor for the LogDatabase class.
 *
 * @param[in] tDateTime PalmOS structure holding the time the database was
 *                        created.
 */
LogDatabase::LogDatabase( DateTimeType tDateTime ) :
    m_LastError( errNone ),
    m_nDbRecordCount( 0 ),
    m_nRealTimeUpdateLength( 0 ),
    m_pRecordBuffer( NULL )
{
    LocalID databaseID;
    UInt16  nAttributes = dmHdrAttrBackup;

    CreateDatabaseName( tDateTime,
                        m_DatabaseName );

    m_LastError = DmCreateDatabase( 0,
                                    m_DatabaseName,
                                    'ABRD',
                                    'Data',
                                    false );

    if ( m_LastError == errNone )
    {
        databaseID = DmFindDatabase( 0,
                                     m_DatabaseName );
        m_LastError = DmSetDatabaseInfo( 0,
                                         databaseID,
                                         NULL,
                                         &nAttributes,
                                         NULL,
                                         NULL,
                                         NULL,
                                         NULL,
                                         NULL,
                                         NULL,
                                         NULL,
                                         NULL,
                                         NULL );
    }
    if ( m_LastError == errNone )
    {
        m_Database = DmOpenDatabase( 0,
                                     databaseID,
                                     dmModeReadWrite );
    }
}

/**
 * Destructor for the LogDatabase class.
 */
LogDatabase::~LogDatabase()
{
    if ( m_Database )
    {
        m_LastError = DmCloseDatabase( m_Database );
    }

    if ( m_pRecordBuffer )
    {
        delete[] m_pRecordBuffer;
    }
}

/**
 * Static function to convert the time structure given into the name of a
 *   database.
 *
 * @param[in]  tDateTime PalmOS structure representing the database creation
 *                        time.
 * @param[out] pNameBuffer Character buffer to hold the database name.  Should
 *                           be of size dmDBNameLength.
 */
void LogDatabase::CreateDatabaseName( DateTimeType tDateTime,
                                      Char*        pNameBuffer )
{
    Char bufDateString[ dateStringLength + 1];
    Char bufTimeString[ timeStringLength + 1];
    Char bufSecondsString[ maxStrIToALen ];

    DateToAscii( tDateTime.month,
                 tDateTime.day,
                 tDateTime.year,
                 dfMDYWithDashes,
                 bufDateString );

    TimeToAscii( tDateTime.hour,
                 tDateTime.minute,
                 tfDot24h,
                 bufTimeString );

    StrIToA( bufSecondsString,
             tDateTime.second );

    StrCopy( pNameBuffer,
             bufDateString );

    StrCat( pNameBuffer,
            "_" );

    StrCat( pNameBuffer,
            bufTimeString );

    StrCat( pNameBuffer,
            "." );

    StrCat( pNameBuffer,
            bufSecondsString );
}

/**
 * Static function to convert a PalmOS time structure to UNIX time.
 *
 * @param[in] pDateTime Pointer to the time structure to convert.
 *
 * @return The number of seconds since midnight on Jan 1, 1970 corresponding
 *           to the input parameter.
 */
UInt32 LogDatabase::GetUnixTime( const DateTimeType* pDateTime )
{
    return TimDateTimeToSeconds( pDateTime ) - PALM_TO_UNIX_TIME_OFFSET;
}

void LogDatabase::WriteHeader( DateTimeType tDateTime,
                               const Char*  pFirmwareSignature,
                               UInt16       nRealTimeUpdateLength )
{
    MemHandle newRecordHandle;
    UInt8*    pHeaderRecord = NULL;
    UInt16    nNewIndex     = dmMaxRecordIndex;

    m_nRealTimeUpdateLength = nRealTimeUpdateLength;
    m_pRecordBuffer         = new UInt8[ m_nRealTimeUpdateLength + sizeof(T_RecordHeaderType) ];
    newRecordHandle         = DmNewRecord( m_Database,
                                           &nNewIndex,
                                           LOG_HEADER_LENGTH );
    if ( newRecordHandle == 0 )
    {
        m_LastError = DmGetLastErr();
        return;
    }

    m_nDbRecordCount++;
    pHeaderRecord = (UInt8*)MemHandleLock( newRecordHandle );

    // File Format
    m_pRecordBuffer[0] = 'F';
    m_pRecordBuffer[1] = 'R';
    m_pRecordBuffer[2] = 'D';

    // Format Version
    *(UInt16*)(m_pRecordBuffer + 6) = FRD_FORMAT_VERSION;

    // Timestamp
    *(UInt32*)(m_pRecordBuffer + 8) = GetUnixTime( &tDateTime );

    // Firmware Signature
    StrCopy( (Char*)(m_pRecordBuffer + 12),
             pFirmwareSignature );

    // Data Begin Index
    m_pRecordBuffer[75] = 0;
    m_pRecordBuffer[76] = 0;
    m_pRecordBuffer[77] = 0;
    m_pRecordBuffer[78] = LOG_HEADER_LENGTH;

    // Output Length
    m_pRecordBuffer[79] = (UInt8)((nRealTimeUpdateLength >> 8) & 0xFF);
    m_pRecordBuffer[80] = (UInt8)(nRealTimeUpdateLength & 0xFF);

    m_LastError = DmWrite( pHeaderRecord,
                           0,
                           m_pRecordBuffer,
                           LOG_HEADER_LENGTH );

    if ( m_LastError == errNone )
    {
        MemHandleUnlock( newRecordHandle );
        m_LastError = DmReleaseRecord( m_Database,
                                       nNewIndex,
                                       true );
    }
}

/**
 * Writes an output record to the log database.
 *
 * @param[in] pData The data payload of the Real Time Update to write to the
 *                    database.
 */
void LogDatabase::WriteOutputRecord( const UInt8* pData )
{
    MemHandle           newRecordHandle;
    UInt8*              pOutputRecord       = NULL;
    UInt16              nNewIndex           = dmMaxRecordIndex;
    T_RecordHeaderType* pOutputRecordHeader = NULL;

    newRecordHandle = DmNewRecord( m_Database,
                                   &nNewIndex,
                                   sizeof(T_RecordHeaderType) + m_nRealTimeUpdateLength );
    if ( newRecordHandle == 0 )
    {
        m_LastError = DmGetLastErr();
        return;
    }

    m_nDbRecordCount++;
    pOutputRecord       = (UInt8*)MemHandleLock( newRecordHandle );

    pOutputRecordHeader = (T_RecordHeaderType*)m_pRecordBuffer;

    pOutputRecordHeader->recordType = OUTPUT_RECORD;
    pOutputRecordHeader->counter    = m_nDbRecordCount - 2; // Subtract off one for the header record, and one for the current record

    m_LastError = DmWrite( pOutputRecord,
                           0,
                           m_pRecordBuffer,
                           sizeof(T_RecordHeaderType) );
    if ( m_LastError == errNone )
    {
        m_LastError = DmWrite( pOutputRecord,
                               sizeof(T_RecordHeaderType),
                               pData,
                               m_nRealTimeUpdateLength );
    }
    if ( m_LastError == errNone )
    {
        MemHandleUnlock( newRecordHandle );
        m_LastError = DmReleaseRecord( m_Database,
                                       nNewIndex,
                                       true );
    }
}

/**
 * Returns the last error encountered by the object.
 *
 * @return PalmOS error code with the last error encountered.
 */
Err LogDatabase::GetLastError( void ) const
{
    return m_LastError;
}